package com.yuelao.yuelao_backend.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yuelao.yuelao_backend.common.ApiResponse;
import com.yuelao.yuelao_backend.common.BizException;
import com.yuelao.yuelao_backend.common.ErrorCode;
import com.yuelao.yuelao_backend.dto.UserDTO;
import com.yuelao.yuelao_backend.dto.request.LoginRequest;
import com.yuelao.yuelao_backend.dto.request.RefreshTokenRequest;
import com.yuelao.yuelao_backend.dto.request.RegisterRequest;
import com.yuelao.yuelao_backend.dto.request.SubmitGuideRequest;
import com.yuelao.yuelao_backend.dto.response.LoginResponse;
import com.yuelao.yuelao_backend.dto.response.TokenRefreshResponse;
import com.yuelao.yuelao_backend.entity.User;
import com.yuelao.yuelao_backend.service.AuthService;
import com.yuelao.yuelao_backend.service.UserService;
import com.yuelao.yuelao_backend.service.InviteService;
import com.yuelao.yuelao_backend.util.JwtUtil;
import com.yuelao.yuelao_backend.util.UserContextUtil;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

@RestController
@RequestMapping("/auth")
@Slf4j
public class AuthController {

    @Resource
    private AuthService authService;

    @Resource
    private UserService userService;

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private AuthenticationManager authenticationManager;
    
    @Resource
    private com.yuelao.yuelao_backend.service.VerificationCodeService verificationCodeService;

    @Resource
    private InviteService inviteService;

    /**
     * 用户登录
     * POST /auth/login
     * @param request 登录请求
     * @return 登录成功后的用户信息和Token
     */
    @PostMapping("/login")
    public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
        try {
            // 1. 使用 AuthenticationManager 进行认证
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.getPhone(), request.getPassword())
            );

            // 2. 将认证信息存入 SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 3. 从认证信息中获取 UserDetails
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            String phone = userDetails.getUsername();

            // 4. 生成 Access Token 和 Refresh Token
            String accessToken = jwtUtil.generateAccessToken(userDetails);
            String refreshToken = jwtUtil.generateRefreshToken(userDetails);

            // 5. 获取用户实体，更新最后登录时间，并转换为 DTO
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
            User user = userService.getOne(queryWrapper);
            if (user == null) {
                throw new BizException(ErrorCode.SYSTEM_ERROR, "认证后无法找到用户");
            }
            user.setLastLoginTime(LocalDateTime.now());
            userService.updateById(user);
            UserDTO userDTO = userService.convertToUserDTO(user);

            // 6. 构建并返回 LoginResponse
            LoginResponse loginResponse = LoginResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .user(userDTO)
                    .build();

            return ApiResponse.ok(loginResponse, "登录成功");
        } catch (org.springframework.security.core.AuthenticationException e) {
            // 认证失败，返回错误信息
            throw new BizException(ErrorCode.NO_AUTH_ERROR, "用户名或密码错误");
        }
    }

    /**
     * 用户注册
     * POST /auth/register
     * @param request 注册请求
     * @return 注册成功后的用户信息和Token
     */
    @PostMapping("/register")
    public ApiResponse<LoginResponse> register(@Valid @RequestBody RegisterRequest request) {
        if (!request.getPassword().equals(request.getConfirmPassword())) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        // 1. 调用注册服务
        UserDTO userDTO = authService.register(request);

        // 2. 从数据库加载新创建的用户信息以生成Token
        UserDetails userDetails = userDetailsService.loadUserByUsername(request.getPhone());

        // 3. 生成 Access Token 和 Refresh Token
        String accessToken = jwtUtil.generateAccessToken(userDetails);
        String refreshToken = jwtUtil.generateRefreshToken(userDetails);

        // 4. 构建并返回 LoginResponse
        LoginResponse loginResponse = LoginResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .user(userDTO)
                .build();

        if (org.springframework.util.StringUtils.hasText(request.getInviteCode())) {
            inviteService.registerWithInvite(request.getInviteCode(), userDTO.getId());
        }

        return ApiResponse.ok(loginResponse, "注册成功");
    }

    /**
     * 刷新 Access Token
     * POST /auth/refresh
     * @param request 包含 Refresh Token 的请求
     * @return 新的 Access Token 和 Refresh Token
     */
    @PostMapping("/refresh")
    public ApiResponse<TokenRefreshResponse> refreshToken(@Valid @RequestBody RefreshTokenRequest request) {
        String refreshToken = request.getRefreshToken();

        // 1. 验证 Refresh Token 是否仍然有效
        if (!jwtUtil.isTokenValid(refreshToken)) {
            throw new BizException(ErrorCode.TOKEN_EXPIRED_ERROR, "Refresh Token 已过期，请重新登录");
        }

        // 2. 从 Refresh Token 中提取用户名（即使 Access Token 已过期）
        String username;
        try {
            username = jwtUtil.extractUsername(refreshToken);
            log.debug("从refresh token中提取到用户名: {}", username);
        } catch (Exception e) {
            log.error("从refresh token提取用户名失败", e);
            throw new BizException(ErrorCode.NO_AUTH_ERROR, "Refresh Token 无效");
        }

        // 3. 加载用户详情
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        // 4. 生成新的 Access Token 和 Refresh Token（滚动刷新）
        String newAccessToken = jwtUtil.generateAccessToken(userDetails);
        String newRefreshToken = jwtUtil.generateRefreshToken(userDetails);

        TokenRefreshResponse response = TokenRefreshResponse.builder()
                .accessToken(newAccessToken)
                .refreshToken(newRefreshToken)
                .build();

        return ApiResponse.ok(response, "Token 刷新成功");
    }

    /**
     * 提交用户引导信息
     * POST /auth/guide
     * @param request 引导信息请求
     * @return 是否成功
     */
    @PostMapping("/guide")
    public ApiResponse<Boolean> submitGuide(@Valid @RequestBody SubmitGuideRequest request) {
        // 从 JWT Token 中获取当前用户ID
        Long userId = UserContextUtil.getCurrentUserId();
        boolean result = authService.submitGuide(userId, request);
        return ApiResponse.ok(result);
    }

    /**
     * 发送验证码
     * POST /auth/send-code
     * @param request 发送验证码请求
     * @return 验证码有效期
     */
    @PostMapping("/send-code")
    public ApiResponse<java.util.Map<String, Object>> sendCode(@Valid @RequestBody SendCodeRequest request) {
        boolean success = verificationCodeService.sendCode(request.getPhone(), request.getType());
        
        if (!success) {
            throw new BizException(ErrorCode.OPERATION_ERROR, "验证码发送失败");
        }
        
        java.util.Map<String, Object> result = new java.util.HashMap<>();
        result.put("expire_time", 300); // 5分钟 = 300秒
        
        return ApiResponse.ok(result, "验证码已发送");
    }

    /**
     * 绑定手机号
     * POST /auth/bind-phone
     * @param request 绑定手机号请求
     * @return 是否成功
     */
    @PostMapping("/bind-phone")
    public ApiResponse<Boolean> bindPhone(@Valid @RequestBody BindPhoneRequest request) {
        // 1. 验证验证码
        boolean isValid = verificationCodeService.verifyCode(
            request.getPhone(), 
            request.getCode(), 
            "bind_phone"
        );
        
        if (!isValid) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "验证码无效或已过期");
        }
        
        // 2. 绑定手机号到当前账号
        // TODO: 实现绑定逻辑，从Token中获取当前用户ID
        Long userId = com.yuelao.yuelao_backend.util.UserContextUtil.getCurrentUserId();
        
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId, userId);
        User user = userService.getOne(wrapper);
        
        if (user == null) {
            throw new BizException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        
        // 检查手机号是否已被其他用户绑定
        LambdaQueryWrapper<User> phoneWrapper = new LambdaQueryWrapper<>();
        phoneWrapper.eq(User::getPhone, request.getPhone());
        User existingUser = userService.getOne(phoneWrapper);
        
        if (existingUser != null && !existingUser.getId().equals(userId)) {
            throw new BizException(ErrorCode.OPERATION_ERROR, "该手机号已被其他用户绑定");
        }
        
        user.setPhone(request.getPhone());
        boolean result = userService.updateById(user);
        
        return ApiResponse.ok(result, "手机号绑定成功");
    }

    /**
     * 微信小程序登录
     * POST /auth/wechat-miniprogram-login
     * @param request 小程序登录请求
     * @return 登录结果（包含token）
     */
    @PostMapping("/wechat-miniprogram-login")
    public ApiResponse<LoginResponse> wechatMiniprogramLogin(@Valid @RequestBody WechatMiniprogramLoginRequest request) {
        // TODO: 实现微信小程序登录逻辑
        // 1. 使用code调用微信接口获取openid和session_key
        // 2. 根据openid查询或创建用户
        // 3. 生成JWT token
        // 4. 返回用户信息和token
        
        log.warn("微信小程序登录功能尚未实现，需要配置微信开放平台");
        throw new BizException(ErrorCode.SYSTEM_ERROR, "微信小程序登录功能暂未开放，请使用其他方式登录");
        
        // 实现示例（需要配置后取消注释）:
        /*
        String openid = wechatService.getOpenid(request.getCode());
        User user = authService.loginOrRegisterByWechat(openid, request.getUserInfo());
        String token = jwtUtil.generateToken(user.getId());
        
        LoginResponse response = LoginResponse.builder()
                .token(token)
                .userId(user.getId())
                .build();
        return ApiResponse.ok(response, "登录成功");
        */
    }

    /**
     * 一键登录（APP运营商）
     * POST /auth/onekey-login
     * @param request 一键登录请求
     * @return 登录结果（包含token）
     */
    @PostMapping("/onekey-login")
    public ApiResponse<LoginResponse> onekeyLogin(@Valid @RequestBody OnekeyLoginRequest request) {
        // TODO: 实现一键登录逻辑
        // 1. 验证运营商token
        // 2. 获取手机号
        // 3. 根据手机号查询或创建用户
        // 4. 生成JWT token
        // 5. 返回用户信息和token
        
        log.warn("一键登录功能尚未实现，需要集成运营商SDK");
        throw new BizException(ErrorCode.SYSTEM_ERROR, "一键登录功能暂未开放，请使用其他方式登录");
        
        // 实现示例（需要配置后取消注释）:
        /*
        String phone = mobileAuthService.getPhoneNumber(request.getAccessToken());
        User user = authService.loginOrRegisterByPhone(phone);
        String token = jwtUtil.generateToken(user.getId());
        
        LoginResponse response = LoginResponse.builder()
                .token(token)
                .userId(user.getId())
                .build();
        return ApiResponse.ok(response, "登录成功");
        */
    }

    /**
     * 发送验证码请求DTO
     */
    @lombok.Data
    public static class SendCodeRequest {
        @NotBlank(message = "手机号不能为空")
        @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
        private String phone;
        @NotBlank(message = "类型不能为空")
        private String type; // login/register/reset_password/change_phone/bind_phone
    }

    /**
     * 微信小程序登录请求DTO
     */
    @lombok.Data
    public static class WechatMiniprogramLoginRequest {
        @jakarta.validation.constraints.NotBlank(message = "code不能为空")
        private String code; // 微信登录凭证
        
        private java.util.Map<String, Object> userInfo; // 用户信息（可选）
    }

    /**
     * 一键登录请求DTO
     */
    @lombok.Data
    public static class OnekeyLoginRequest {
        @jakarta.validation.constraints.NotBlank(message = "accessToken不能为空")
        private String accessToken; // 运营商返回的token
    }

    /**
     * 绑定手机号请求DTO
     */
    @lombok.Data
    public static class BindPhoneRequest {
        @jakarta.validation.constraints.NotBlank(message = "手机号不能为空")
        @jakarta.validation.constraints.Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
        private String phone;
        
        @jakarta.validation.constraints.NotBlank(message = "验证码不能为空")
        private String code;
    }
}
